CVE-2026-49346: fix integer overflow in image plane allocation size
authorDebian Multimedia Maintainers <debian-multimedia@lists.debian.org>
Thu, 6 Aug 2026 05:05:03 +0000 (13:05 +0800)
committerAron Xu <aron@debian.org>
Thu, 6 Aug 2026 05:05:03 +0000 (13:05 +0800)
Origin: upstream, https://github.com/strukturag/libde265/commit/8a1b5cf212f78e1c77cb46eb5d56e492a9336eb8
Bug: https://github.com/strukturag/libde265/security/advisories/GHSA-vv8h-932h-7r86
Bug-Debian: https://bugs.debian.org/1140431
Applied-Upstream: 1.1.0

Large SPS dimensions with 16-bit bit depth overflow the 32-bit allocation
size in de265_image_get_buffer(); the wrapped ~1 KB allocation is later
written with the full plane size, corrupting the heap.

Gbp-Pq: Name CVE-2026-49346.patch

libde265/image.cc

index 0ae775118ba6450b1b6152902fd2ac34466a69ef..9ddfed2c3bfd6da123bf5cc02ad9a2fa74040df0 100644 (file)
@@ -71,10 +71,11 @@ LIBDE265_API void* de265_alloc_image_plane(struct de265_image* img, int cIdx,
                                            void* inputdata, int inputstride, void *userdata)
 {
   int alignment = STANDARD_ALIGNMENT;
-  int stride = (img->get_width(cIdx) + alignment-1) / alignment * alignment;
-  int height = img->get_height(cIdx);
+  uint32_t stride = (img->get_width(cIdx) + alignment-1) / alignment * alignment;
+  uint32_t height = img->get_height(cIdx);
 
-  uint8_t* p = (uint8_t *)ALLOC_ALIGNED_16(stride * height + MEMORY_PADDING);
+  // size computed in size_t: stride*height can exceed UINT32_MAX for large planes
+  uint8_t* p = static_cast<uint8_t*>(ALLOC_ALIGNED_16(static_cast<size_t>(stride) * height + MEMORY_PADDING));
 
   if (p==NULL) { return NULL; }
 
@@ -82,13 +83,15 @@ LIBDE265_API void* de265_alloc_image_plane(struct de265_image* img, int cIdx,
 
   // copy input data if provided
 
-  if (inputdata != NULL) {
-    if (inputstride == stride) {
-      memcpy(p, inputdata, stride*height);
+  if (inputdata != nullptr) {
+    if (inputstride == static_cast<int>(stride)) {
+      memcpy(p, inputdata, static_cast<size_t>(stride) * height);
     }
     else {
-      for (int y=0;y<height;y++) {
-        memcpy(p+y*stride, ((char*)inputdata) + inputstride*y, inputstride);
+      for (uint32_t y=0;y<height;y++) {
+        memcpy(p + static_cast<size_t>(y) * stride,
+               static_cast<char*>(inputdata) + static_cast<size_t>(inputstride) * y,
+               inputstride);
       }
     }
   }
@@ -108,30 +111,35 @@ LIBDE265_API void de265_free_image_plane(struct de265_image* img, int cIdx)
 static int  de265_image_get_buffer(de265_decoder_context* ctx,
                                    de265_image_spec* spec, de265_image* img, void* userdata)
 {
-  const int rawChromaWidth  = spec->width  / img->SubWidthC;
-  const int rawChromaHeight = spec->height / img->SubHeightC;
+  const uint32_t rawChromaWidth  = spec->width  / img->SubWidthC;
+  const uint32_t rawChromaHeight = spec->height / img->SubHeightC;
 
-  int luma_stride   = (spec->width    + spec->alignment-1) / spec->alignment * spec->alignment;
-  int chroma_stride = (rawChromaWidth + spec->alignment-1) / spec->alignment * spec->alignment;
+  uint32_t luma_stride   = (spec->width    + spec->alignment-1) / spec->alignment * spec->alignment;
+  uint32_t chroma_stride = (rawChromaWidth + spec->alignment-1) / spec->alignment * spec->alignment;
 
   assert(img->BitDepth_Y >= 8 && img->BitDepth_Y <= 16);
   assert(img->BitDepth_C >= 8 && img->BitDepth_C <= 16);
 
-  int luma_bpl   = luma_stride   * ((img->BitDepth_Y+7)/8);
-  int chroma_bpl = chroma_stride * ((img->BitDepth_C+7)/8);
+  uint32_t luma_bpl   = luma_stride   * ((img->BitDepth_Y+7)/8);
+  uint32_t chroma_bpl = chroma_stride * ((img->BitDepth_C+7)/8);
 
-  int luma_height   = spec->height;
-  int chroma_height = rawChromaHeight;
+  uint32_t luma_height   = spec->height;
+  uint32_t chroma_height = rawChromaHeight;
 
   bool alloc_failed = false;
 
-  uint8_t* p[3] = { 0,0,0 };
-  p[0] = (uint8_t *)ALLOC_ALIGNED_16(luma_height   * luma_bpl   + MEMORY_PADDING);
-  if (p[0]==NULL) { alloc_failed=true; }
+  // Compute the plane sizes in size_t. Each operand fits in uint32_t, but the
+  // height * bytes-per-line product can exceed UINT32_MAX for large frames, so
+  // the multiplication must be done in 64 bits. Computing it in 32 bits wraps
+  // the allocation size to a small value while fill_image() later writes the
+  // real (size_t) size -> heap buffer overflow (GHSA-vv8h-932h-7r86).
+  uint8_t* p[3] = { nullptr,nullptr,nullptr };
+  p[0] = static_cast<uint8_t*>(ALLOC_ALIGNED_16(static_cast<size_t>(luma_height) * luma_bpl + MEMORY_PADDING));
+  if (p[0]==nullptr) { alloc_failed=true; }
 
   if (img->get_chroma_format() != de265_chroma_mono) {
-    p[1] = (uint8_t *)ALLOC_ALIGNED_16(chroma_height * chroma_bpl + MEMORY_PADDING);
-    p[2] = (uint8_t *)ALLOC_ALIGNED_16(chroma_height * chroma_bpl + MEMORY_PADDING);
+    p[1] = static_cast<uint8_t*>(ALLOC_ALIGNED_16(static_cast<size_t>(chroma_height) * chroma_bpl + MEMORY_PADDING));
+    p[2] = static_cast<uint8_t*>(ALLOC_ALIGNED_16(static_cast<size_t>(chroma_height) * chroma_bpl + MEMORY_PADDING));
 
     if (p[1]==NULL || p[2]==NULL) { alloc_failed=true; }
   }